package com.future.bs.component.stream;

import com.future.bs.component.business.AbstractBusiness;
import com.future.bs.component.business.Business;
import com.future.bs.context.Context;
import com.future.bs.component.business.BusinessPredicate;
import com.future.bs.component.report.DefaultReport;
import com.future.bs.component.report.Report;
import com.future.bs.context.BusinessStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 条件业务流
 * </p>
 *
 * @author Jingjie
 * @since 2020-11-08 10:12
 */
public class ConditionalStream<T extends Context> extends Stream<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConditionalStream.class);

    private List<Business<T>> businesses;
    private List<BusinessPredicate<T>> businessPredicates;

    @Override
    public Report<T> execute(T context) {
        // 校验断言和业务一一对应
        if (this.businesses.size() != this.businessPredicates.size()) {
            throw new RuntimeException("Predicate and business numbers do not match.");
        }
        Report<T> report = null;
        for (int index = 0; index < this.businesses.size(); index++) {
            // 断言，断言和业务一一对应
            Business<T> business = this.businesses.get(index);
            BusinessPredicate<T> businessPredicate = businessPredicates.get(index);
            if (businessPredicate != null && !businessPredicate.apply(report)) {
                LOGGER.warn("Predicate result is false, skipping current business.");
                continue;
            }
            report = business.execute(context);
            if (report != null && BusinessStatus.FAILED == report.getStatus()) {
                LOGGER.warn("Business stream '{}' has failed, skipping follower business units.", getName());
                return report;
            }
        }
        return report == null ? new DefaultReport<>(BusinessStatus.SUCCESS, context) : report;
    }

    /**
     * <p>
     * 建造器
     * </p>
     *
     * @author Jingjie
     * @since 2020-11-08 09:55
     */
    public class Builder {
        private String name;
        private List<Business<T>> businesses = new ArrayList<>();
        private List<BusinessPredicate<T>> businessPredicates = new ArrayList<>();

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder process(BusinessPredicate<T> predicate, AbstractBusiness<T> business) {
            this.businessPredicates.add(predicate);
            this.businesses.add(business);
            return this;
        }

        public ConditionalStream<T> build() {
            ConditionalStream.this.name = this.name;
            ConditionalStream.this.businesses = this.businesses;
            ConditionalStream.this.businessPredicates = this.businessPredicates;
            return ConditionalStream.this;
        }
    }
}
